#pragma once

template<typename T>  //T is a dual interface (inherits from IDispatch)
class TDispatch : public T
{
    TDispatch(const TDispatch&);
    TDispatch& operator=(const TDispatch&);
    
protected:

    TDispatch();    
    virtual ~TDispatch();
    
    HRESULT Initialize(ITypeLib*);
    
public:

    bool Initialized() const;

    HRESULT STDMETHODCALLTYPE GetTypeInfoCount( 
        UINT *pctinfo);
    
    HRESULT STDMETHODCALLTYPE GetTypeInfo( 
        UINT iTInfo,
        LCID lcid,
        ITypeInfo **ppTInfo);
    
    HRESULT STDMETHODCALLTYPE GetIDsOfNames( 
        REFIID riid,
        LPOLESTR *rgszNames,
        UINT cNames,
        LCID lcid,
        DISPID *rgDispId);
    
    HRESULT STDMETHODCALLTYPE Invoke( 
        DISPID dispIdMember,
        REFIID riid,
        LCID lcid,
        WORD wFlags,
        DISPPARAMS *pDispParams,
        VARIANT *pVarResult,
        EXCEPINFO *pExcepInfo,
        UINT *puArgErr);
        
protected:

    //ITypeLib* m_pTypeLib;
    ITypeInfo* m_pTypeInfo;

};


template<typename T>
inline TDispatch<T>::TDispatch() :
    //m_pTypeLib(0),
    m_pTypeInfo(0)
{
}


template<typename T>
inline TDispatch<T>::~TDispatch()
{
    if (m_pTypeInfo)
        m_pTypeInfo->Release();
        
    //if (m_pTypeLib)
    //    m_pTypeLib->Release();
}


template<typename T>
inline bool TDispatch<T>::Initialized() const
{
    return m_pTypeInfo ? true : false;
}


template<typename T>
HRESULT TDispatch<T>::Initialize(ITypeLib* pTypeLib)
{
    if (pTypeLib == 0)
        return E_POINTER;
        
    if (m_pTypeInfo)
        return E_FAIL;  //something's wrong
        
    const HRESULT hr = pTypeLib->GetTypeInfoOfGuid(__uuidof(T), &m_pTypeInfo);
        
    return hr;
}    


template<typename T>
HRESULT TDispatch<T>::GetTypeInfoCount(UINT* p)
{
    if (p == 0)
        return E_POINTER;

    UINT& c = *p;
    
    c = m_pTypeInfo ? 1 : 0;
    return S_OK;
}

template<typename T>
HRESULT TDispatch<T>::GetTypeInfo( 
    UINT index,
    LCID,
    ITypeInfo** pp)
{
    if (pp == 0)
        return E_POINTER;
        
    ITypeInfo*& p = *pp;
    p = 0;
    
    if (m_pTypeInfo == 0)
        return DISP_E_BADINDEX;
        
    if (index > 0)
        return DISP_E_BADINDEX;

    p = m_pTypeInfo;
    p->AddRef();
    
    return S_OK;
}    
    

template<typename T>
HRESULT TDispatch<T>::GetIDsOfNames( 
    REFIID,
    LPOLESTR *rgszNames,
    UINT cNames,
    LCID,
    DISPID *rgDispId)
{
    return DispGetIDsOfNames(m_pTypeInfo, rgszNames, cNames, rgDispId);
}


template<typename T>
HRESULT TDispatch<T>::Invoke( 
    DISPID dispIdMember,
    REFIID,
    LCID,
    WORD wFlags,
    DISPPARAMS *pDispParams,
    VARIANT *pVarResult,
    EXCEPINFO *pExcepInfo,
    UINT *puArgErr)
{
    return DispInvoke(
            static_cast<T*>(this),
            m_pTypeInfo, 
            dispIdMember, 
            wFlags, 
            pDispParams, 
            pVarResult, 
            pExcepInfo, 
            puArgErr);
}
